

/******************************************************************************

 Copyright (c) 2012-2016, Texas Instruments Incorporated
 All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions
 are met:

 *  Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.

 *  Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.

 *  Neither the name of Texas Instruments Incorporated nor the names of
    its contributors may be used to endorse or promote products derived
    from this software without specific prior written permission.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

 ******************************************************************************
 Release Name: ble_sdk_2_02_00_31
 Release Date: 2016-06-16 18:57:29
 *****************************************************************************/

/*********************************************************************
 * INCLUDES
 */
#include <string.h>

#ifdef DEVICE_FAMILY
    #undef DEVICE_FAMILY_PATH
    #define DEVICE_FAMILY_PATH(x) <ti/devices/DEVICE_FAMILY/x>
    #include DEVICE_FAMILY_PATH(driverlib/rom.h)
    #include DEVICE_FAMILY_PATH(driverlib/vims.h)
    #include DEVICE_FAMILY_PATH(driverlib/flash.h)
#else
    #error "You must define DEVICE_FAMILY at the project level as one of cc26x0, cc26x0r2, cc13x0, etc."
#endif


#include "oad_target.h"
#include "oad.h"

/*********************************************************************
 * CONSTANTS
 */
#define ERROR_BLOCK     0xFFFF

#define HAL_FLASH_WORD_SIZE  4

/*********************************************************************
 * MACROS
 */

/*********************************************************************
 * GLOBAL VARIABLES
 */

/*********************************************************************
 * LOCAL VARIABLES
 */

static oadGetNextBlockReqCb_t OAD_getNextBlockReqCb = NULL;
static oadRejectImageCb_t OAD_rejectImageCb = NULL;
static oadSendStatusCb_t OAD_sendStatusCb = NULL;

static uint16_t oadBlkNum = 0;
 uint16_t oadBlkTot = 0xFFFF;
static uint32_t imageAddress;
static uint16_t imagePage;
static uint32_t flashPageSize;

#ifndef FEATURE_OAD_ONCHIP
// Used to keep track of images written.
static uint8_t flagRecord = 0;
#endif //FEATURE_OAD_ONCHIP

static uint8_t oad_imageIdLen = 0;

// Flash consists of 32 pages of 4 KB.
#define HAL_FLASH_WORD_SIZE       4

/*********************************************************************
 * LOCAL FUNCTIONS
 */

#if !defined FEATURE_OAD_ONCHIP
static uint8_t checkDL(void);
static uint16_t crcCalcDL(void);
static uint16_t crc16(uint16_t crc, uint8_t val);
#endif  // !FEATURE_OAD_ONCHIP


/*********************************************************************
 * @fn      OAD_register
 *
 * @brief   Register a callback function with the OAD Target Profile.
 *
 * @param   *pfnOadCBs - write callback function container.
 *
 * @return  None.
 */
void OAD_init(oadCBs_t *pfnOadCBs)
{

  // Set flash size variables
  //replacing #define HAL_FLASH_PAGE_SIZE       4096
  flashPageSize = FlashSectorSizeGet();

  // Register callback functions.
  OAD_getNextBlockReqCb = pfnOadCBs->pfnOadGetNextBlockReqCb;
  OAD_rejectImageCb = pfnOadCBs->pfnOadRejectImageCb;
  OAD_sendStatusCb = pfnOadCBs->pfnOadSendStatusCb;
}

/*********************************************************************
 * @fn      OAD_imgIdentifyWrite
 *
 * @brief   Process the Image Identify Write.  Determined if the image
 *          header identified here should or should not be downloaded by
 *          this application.
 *
 * @param   connHandle - connection message was received on
 * @param   pValue     - pointer to image header data
 *
 * @return  true image accepted, false Image rejected
 */
uint8_t OAD_imgIdentifyWrite(uint8_t *pValue)
{
  uint8_t imageHeaderAccepted = 0;
  img_hdr_t ImgHdr;
  uint8_t hdrOffset = oad_imageIdLen == 8 ? 0 : 4;

  // Store the new image's header
  OADTarget_storeImageHeader(pValue);

  // Read out running image's header.
  OADTarget_getCurrentImageHeader(&ImgHdr);

  // Calculate block total of the new image.
  oadBlkTot = BUILD_UINT16(pValue[hdrOffset + 2], pValue[hdrOffset + 3]) /
              (OAD_BLOCK_SIZE / HAL_FLASH_WORD_SIZE);
  oadBlkNum = 0;
#ifndef FEATURE_OAD_ONCHIP
    flagRecord = 0;
#endif

  /* Requirements to begin OAD:
   * 1) LSB of image version cannot be the same, this would imply a code overlap
   *    between currently running image and new image.
   * 2) Total blocks of new image must not exceed maximum blocks supported, else
   *    the new image cannot fit.
   * 3) Block total must be greater than 0.
   * 4) Optional: Add additional criteria for initiating OAD here.
   */
  if (OADTarget_validateNewImage(pValue + hdrOffset, &ImgHdr, oadBlkTot))
  {
    // Determine where image will be stored.
    imageAddress = OADTarget_imageAddress(pValue+hdrOffset);
    imagePage = imageAddress / FlashSectorSizeGet();

    // Open the target interface
    if (OADTarget_open())
    {
        // Image accepted, request block 0.
        OAD_getNextBlockReqCb(0);

        imageHeaderAccepted = 1;
    }
    else
    {
        // Opening the flash has failed; report error
        OAD_sendStatusCb(OAD_FLASH_ERR);
    }
  }
  else
  {
    // Image rejected, send header information of currently running image
    // to OAD manager.
    OAD_rejectImageCb(&ImgHdr);
  }

  return imageHeaderAccepted;
}

/*********************************************************************
 * @fn      OAD_imgBlockWrite
 *
 * @brief   Process the Image Block Write.
 *
 * @param   pValue - pointer to data to be written
 *
 * @return  none
 */
void OAD_imgBlockWrite(uint8_t *pValue)
{
  // N.B. This must be left volatile.
  volatile uint16_t blkNum = BUILD_UINT16(pValue[0], pValue[1]);

  // Check that this is the expected block number.
  if (oadBlkNum == blkNum)
  {
    // Calculate address to write as (start of OAD range) + (offset into range)
    uint32_t addr = imageAddress + (oadBlkNum * OAD_BLOCK_SIZE);

    // If address starts a new page, erase that page first.
    if ((addr % flashPageSize) == 0)
    {
      OADTarget_eraseFlash(addr / flashPageSize);
    }

    // Write a 16 byte block to Flash.
    OADTarget_writeFlash(imagePage, (blkNum * OAD_BLOCK_SIZE), pValue+2,
                         OAD_BLOCK_SIZE);

    // Increment received block count.
    oadBlkNum++;
  }
  else
  {
    // Overflow, abort OAD
    oadBlkNum = 0;
#ifndef FEATURE_OAD_ONCHIP
    flagRecord = 0;
#endif
    // Close the target device
    OADTarget_close();

    // Send status
    OAD_sendStatusCb(OAD_BUFFER_OFL);

    return;
  }

  // Check if the OAD Image is complete.
  if (oadBlkNum == oadBlkTot)
  {
#if FEATURE_OAD_ONCHIP
    // Handle CRC verification in BIM.
    OADTarget_systemReset();
#else // !FEATURE_OAD_ONCHIP
    // Run CRC check on new image.
    if (checkDL())
    {
      // Store the flag of the downloaded image.
      flagRecord |= getImageFlag();

      // Store the image information.
      saveImageInfo();

      // Indicate a successful download and CRC
      OAD_sendStatusCb(OAD_SUCCESS);
    }
    else
    {
      // CRC error
      OAD_sendStatusCb(OAD_CRC_ERR);
    }
    flagRecord = 0;
#endif //FEATURE_OAD_ONCHIP

    OADTarget_close();
    oadBlkNum = 0;
  }
  else
  {
    // Request the next OAD Image block.
    OAD_getNextBlockReqCb(oadBlkNum);
  }
}

#if !defined FEATURE_OAD_ONCHIP

/*********************************************************************
 * @fn      crcCalcDL
 *
 * @brief   Run the CRC16 Polynomial calculation over the DL image.
 *
 * @param   None
 *
 * @return  The CRC16 calculated.
 */
static uint16_t crcCalcDL(void)
{
  uint16_t imageCRC = 0;
  uint8_t page;
  uint32_t flashPageSize = FlashSectorSizeGet();
  uint32_t blocksPerPage = flashPageSize / OAD_BLOCK_SIZE;

  uint8_t lastPage = oadBlkTot / blocksPerPage;

  // Remainder of bytes not divisible by the size of a flash page in bytes.
  uint16_t numRemBytes = (oadBlkTot - (lastPage * blocksPerPage))
                         * OAD_BLOCK_SIZE;

  // Set last page to end of OAD image address range.
  lastPage += imagePage;

  // Read over downloaded pages
  for (page = imagePage; page <= lastPage; page++)
  {
    uint16_t offset;

    // Read over all flash words in a page, excluding the CRC section of the
    // first page and all bytes after remainder bytes on the last page.
    for (offset = (page == imagePage) ? HAL_FLASH_WORD_SIZE : 0;
         offset < flashPageSize &&
         (page < lastPage || offset < numRemBytes);
         offset += HAL_FLASH_WORD_SIZE)
    {
      uint8_t buf[HAL_FLASH_WORD_SIZE];
      uint8_t idx;

      // Read a word from flash.
      OADTarget_readFlash(page, offset, buf, HAL_FLASH_WORD_SIZE);

      // Calculate CRC of word, byte by byte.
      for (idx = 0; idx < HAL_FLASH_WORD_SIZE; idx++)
      {
        imageCRC = crc16(imageCRC, buf[idx]);
      }
    }
  }

  // IAR note explains that poly must be run with value zero for each byte of
  // the crc.
  imageCRC = crc16(imageCRC, 0);
  imageCRC = crc16(imageCRC, 0);

  // Return the CRC calculated over the image.
  return imageCRC;
}

/*********************************************************************
 * @fn      checkDL
 *
 * @brief   Check validity of the downloaded image.
 *
 * @param   None.
 *
 * @return  TRUE or false for image valid.
 */
static uint8_t checkDL(void)
{
  uint16_t crc[2];

  OADTarget_getCrc(crc);

  if ((crc[0] == 0xFFFF) || (crc[0] == 0x0000))
  {
    return false;
  }

  // Calculate CRC of downloaded image.
  crc[1] = crcCalcDL();

  if (crc[1] == crc[0])
  {
    // Set the CRC shadow as equivalent to the CRC.
    OADTarget_setCrc(crc);
  }

  return (crc[0] == crc[1]);
}

/*********************************************************************
 * @fn          crc16
 *
 * @brief       Run the CRC16 Polynomial calculation over the byte parameter.
 *
 * @param       crc - Running CRC calculated so far.
 * @param       val - Value on which to run the CRC16.
 *
 * @return      crc - Updated for the run.
 */
static uint16_t crc16(uint16_t crc, uint8_t val)
{
  const uint16_t poly = 0x1021;
  uint8_t cnt;

  for (cnt = 0; cnt < 8; cnt++, val <<= 1)
  {
    uint8_t msb = (crc & 0x8000) ? 1 : 0;

    crc <<= 1;

    if (val & 0x80)
    {
      crc |= 0x0001;
    }

    if (msb)
    {
      crc ^= poly;
    }
  }

  return crc;
}

#endif // !FEATURE_OAD_ONCHIP

/*********************************************************************
*********************************************************************/
